package com.dreamchaser.familydevice.exception;

import com.alibaba.fastjson.JSON;
import com.dreamchaser.familydevice.constant.ResponseCode;
import com.dreamchaser.familydevice.domain.MessageVO;
import com.dreamchaser.familydevice.util.QYWeChat.WeChatUtil;
import com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * @Author
 * @Description 全局的的异常拦截器（拦截所有的控制器）（带有@RequestMapping注解的方法上都会拦截）
 */
@RestControllerAdvice
@Order(-1)
@Slf4j
public class GlobalExceptionHandler {

    @Autowired
    WeChatUtil weChatUtil;
    @Value("${customConfig.isErrorNotice}")
    Boolean isErrorNotice;
    @Value("${customConfig.toUsers}")
    String toUsers;

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.OK)
    public MessageVO handleException(Exception e) {
        MessageVO messageVo = new MessageVO();


        String statusMsg = "服务器开小差";

        e.printStackTrace();
        // spring mvc 数据绑定异常
        if (e instanceof BindException) {
            log.error("绑定参数异常:", e.getMessage());
            List<ObjectError> allErrors = ((BindException) e).getAllErrors();
            StringBuilder msgList = new StringBuilder("");
            for (ObjectError error : allErrors
            ) {
                statusMsg = error.getDefaultMessage();
                msgList.append(statusMsg + "\r\n");
            }
            messageVo.back(ResponseCode.ERROR_CONDITION, "参数校验失败").setError(msgList.toString());

            // hibernate validator 参数验证异常
        } else if (e instanceof ConstraintViolationException) {
            log.error("验证参数异常:", e.getMessage());
            Set<ConstraintViolation<?>> constraintViolations = ((ConstraintViolationException) e).getConstraintViolations();
            Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();
            StringBuilder msgList = new StringBuilder("");
            while (iterator.hasNext()) {
                ConstraintViolation<?> cvl = iterator.next();
                msgList.append(cvl.getMessageTemplate() + "\r\n");
            }
            messageVo.back(ResponseCode.ERROR_CONDITION, "参数校验失败").setError(msgList.toString());

        } else if (e instanceof MethodArgumentNotValidException) {
            log.error("验证参数异常:", e.getMessage());
            BindingResult bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
            messageVo.back(ResponseCode.ERROR_CONDITION, "参数校验失败").setError(getErrorsMessage(bindingResult));
        } else if (e instanceof DataAccessException) {
            log.error("数据操作异常:", e);
            final Throwable cause = e.getCause();
            // 之后判断cause类型进一步记录日志处理
            if (cause instanceof MySQLIntegrityConstraintViolationException) {
                // Duplicate entry '1@qq.com' for key 'email'
                String msg = cause.getMessage();
                statusMsg = String.format("数据重复，操作失败：%s", msg.substring(16, msg.indexOf("for key")));
                messageVo.back(ResponseCode.EXCEPTION, "数据操作异常：" + statusMsg).setError(getErrorStrackTrace(e));

            } else {
                messageVo.back(ResponseCode.EXCEPTION, "系统错误：数据操作异常").setError(getErrorStrackTrace(e));
            }
            //messageVo.back(ResponseCode.EXCEPTION, "数0据操作异常",new Date()).errorMessage(statusMsg).exceptionMessge(e.toString());
        } else if (e instanceof BusinessException) {
            log.error("业务异常:", e);
            statusMsg = ((BusinessException) e).getMessage();
            messageVo.back(((BusinessException) e).getCode(), statusMsg).setError(getErrorStrackTrace(e.getCause()));
        } else if (e instanceof AuthenticationException) {
            log.error("业务异常:", e);
            statusMsg = ((AuthenticationException) e).getMessage();
            messageVo.back(ResponseCode.ERROR_LOGIN, statusMsg).setError(getErrorStrackTrace(e.getCause()));
        } else {
            log.error("系统异常:", e);
            messageVo.back(ResponseCode.EXCEPTION, "系统错误:未知").setError(getErrorStrackTrace(e));
            if (isErrorNotice) {
                weChatUtil.sendAgentMsg(toUsers, WeChatUtil.bugAgentId, JSON.toJSONString(messageVo));
            }
        }

        return messageVo;
    }


    public static String getErrorStrackTrace(Throwable e) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        try {
            if (e != null) e.printStackTrace(pw);
            return sw.toString();
        } finally {
            pw.close();
        }
    }

    public String getErrorsMessage(BindingResult errors) {
        if (errors.hasErrors()) {
            StringBuilder str = new StringBuilder("");
            for (FieldError item :
                    errors.getFieldErrors()) {
                str.append(item.getDefaultMessage() + "\r\n");
            }
            return str.toString();
        } else {
            return null;
        }
    }
}
